package com.trailwalker.javafx.widget.control.imageview;

import com.trailwalker.javafx.utils.style.AdaptUtils;
import com.trailwalker.javafx.utils.thread.ThreadUtils;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

/**
 * @author Trailwalker
 * @create 2021-10-20 15:13
 */
public class ScaleImageView extends HBox {

    private static final int TRANSPARENT_PIXEL_SIZE = 5;

    private ScaleType mScaleType;
    private double mWidth;
    private double mHeight;
    private Image mSourceImage;
    private boolean mIsBackgroundLoading;
    private boolean mIsShowBackgroundImage = false;
    private ImageView mIvForeGround;
    private AnchorPane apRoot;
    private ImageView mIvBackground;

    public ScaleImageView(double width, double height, ScaleType scaleType) {
        onInit(width, height, scaleType, false);
    }

    public ScaleImageView(double width, double height, ScaleType scaleType, boolean isBackgroundLoading) {
        onInit(width, height, scaleType, isBackgroundLoading);
    }

    public ScaleImageView(double width, double height) {
        onInit(width, height, ScaleType.FIT_CENTER, false);
    }

    public ScaleImageView(double width, double height, boolean isBackgroundLoading) {
        onInit(width, height, ScaleType.FIT_CENTER, isBackgroundLoading);
    }

    private void onInit(double width, double height, ScaleType scaleType, boolean isBackgroundLoading) {
        mWidth = width;
        mHeight = height;
        mScaleType = scaleType;
        mIsBackgroundLoading = isBackgroundLoading;
        initLoad();
        initView();
        initData();
        initListener();
        onReady();
    }

    private void onReady() {

    }

    private void initListener() {

    }

    private void initData() {

    }

    private void initView() {
        AdaptUtils.getInstance().setScalePadding(this, 0);
        this.setAlignment(Pos.CENTER);

        apRoot = new AnchorPane();
        this.getChildren().add(apRoot);

        mIvBackground = new ImageView();
        apRoot.getChildren().add(mIvBackground);
        AnchorPane.setTopAnchor(mIvBackground, 0.0);
        AnchorPane.setLeftAnchor(mIvBackground, 0.0);

        mIvForeGround = new ImageView();
        apRoot.getChildren().add(mIvForeGround);
        AnchorPane.setTopAnchor(mIvForeGround, 0.0);
        AnchorPane.setLeftAnchor(mIvForeGround, 0.0);

        setAllHeight();
        setAllWidth();
    }

    private void setAllHeight() {
        this.setPrefHeight(mHeight);
        this.setMaxHeight(mHeight);
        apRoot.setPrefHeight(mHeight);
        apRoot.setMaxHeight(mHeight);
        mIvBackground.setFitHeight(mHeight);
        mIvForeGround.setFitHeight(mHeight);
    }

    private void setAllWidth() {
        this.setPrefWidth(mWidth);
        this.setMaxWidth(mWidth);
        apRoot.setPrefWidth(mWidth);
        apRoot.setMaxWidth(mWidth);
        mIvBackground.setFitWidth(mWidth);
        mIvForeGround.setFitWidth(mWidth);
    }

    private void initLoad() {

    }

    @Override
    public void setWidth(double width) {
        mWidth = width;
        setAllWidth();
        refreshImageView();
    }

    @Override
    public void setHeight(double height) {
        mHeight = height;
        setAllHeight();
        refreshImageView();
    }

    public void setIsShowBackgroundImage(boolean isShowBackgroundImage) {
        mIsShowBackgroundImage = isShowBackgroundImage;
        refreshImageView();
    }

    public void setScaleType(ScaleType scaleType) {
        mScaleType = scaleType;
        refreshImageView();
    }

    public void setImage(String imageFilePath) {
        if (mIsBackgroundLoading) {
            ThreadUtils.runOnChildThread(new Runnable() {
                @Override
                public void run() {
                    try {
                        BufferedImage bufferedImage = ImageIO.read(new File(imageFilePath));
                        WritableImage writableImage = SwingFXUtils.toFXImage(bufferedImage, null);
                        ThreadUtils.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                setImage(writableImage);
                            }
                        });
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        } else {
            Image image = new Image("file:" + imageFilePath);
            setImage(image);
        }
    }

    public void setImage(Image image) {
        mSourceImage = image;
        refreshImageView();
    }

    private void refreshImageView() {
        WritableImage backgroundWritableImage = null;
        if (mSourceImage != null) {
            if (mIsShowBackgroundImage) {
                backgroundWritableImage = new WritableImage((int) mWidth, (int) mHeight);
                PixelWriter pixelWriter = backgroundWritableImage.getPixelWriter();
                for (int i = 0; i < backgroundWritableImage.getWidth(); i++) {
                    for (int j = 0; j < backgroundWritableImage.getHeight(); j++) {
                        if ((i / TRANSPARENT_PIXEL_SIZE + j / TRANSPARENT_PIXEL_SIZE) % 2 == 0) {
                            pixelWriter.setColor(i, j, Color.valueOf("#cccccc"));
                        } else {
                            pixelWriter.setColor(i, j, Color.valueOf("#ffffff"));
                        }
                    }
                }
            }
            Rectangle2D viewport;
            switch (mScaleType) {
                case FIT_XY:
                    viewport = getFitXyViewport();
                    break;
                case FIT_CENTER:
                    viewport = getFitCenterViewport(mSourceImage, mHeight, mWidth);
                    break;
                default:
                    viewport = getFitCenterViewport(mSourceImage, mHeight, mWidth);
                    break;
            }
            mIvForeGround.setViewport(viewport);
        }
        mIvBackground.setImage(backgroundWritableImage);
        mIvForeGround.setImage(mSourceImage);
    }

    public static Rectangle2D getFitCenterViewport(Image sourceImage, double imageViewHeight, double imageViewWidth) {
        double sourceImageHeight = sourceImage.getHeight();
        double sourceImageWidth = sourceImage.getWidth();

        double scale;
        if (imageViewHeight / imageViewWidth >= sourceImageHeight / sourceImageWidth) {
            scale = sourceImageWidth / imageViewWidth;
        } else {
            scale = sourceImageHeight / imageViewHeight;
        }
        double viewportWidth = imageViewWidth * scale;
        double viewportHeight = imageViewHeight * scale;
        double viewportX = sourceImageWidth / 2 - viewportWidth / 2;
        double viewportY = sourceImageHeight / 2 - viewportHeight / 2;
        Rectangle2D viewportRec = new Rectangle2D(viewportX, viewportY, viewportWidth, viewportHeight);
        return viewportRec;
    }

    private static Rectangle2D getFitXyViewport() {
        return null;
    }

    public boolean isBackgroundLoading() {
        return mIsBackgroundLoading;
    }

    public enum ScaleType {
        FIT_CENTER,
        FIT_XY
    }
}
